Add Xen trace buffer and supporting tools.
3fbd0a42l40lM0IICw2jXbQBVZSdZg tools/xc/py/Xc.c
3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xc/py/XenoUtil.py
3fbd0a40yT6G3M9hMpaz5xTUdl0E4g tools/xc/py/setup.py
+403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile
+403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c
+403a3edb0lzD0Fojc-NYNoXr3SYrnA tools/xentrace/xentrace_cpusplit.py
+403a3edblCUrzSj0mmKhO5HOPrOrSQ tools/xentrace/xentrace_format.py
3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING
3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile
3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk
3ddb79bd0gVQYmL2zvuJnldvD0AGxQ xen/common/softirq.c
3e7f358awXBC3Vw-wFRwPw18qL1khg xen/common/string.c
3ddb79bdQqFHtHRGEO2dsxGgo6eAhw xen/common/timer.c
+403a3edbejm33XLTGMuinKEwQBrOIg xen/common/trace.c
3ddb79bd3zgV33PHdt-cgh3sxcb1hw xen/common/vsprintf.c
3ddb79c0ppNeJtjC4va8j41ADCnchA xen/drivers/Makefile
3ddb79beWzgPS8ozf2BL2g3ZkiWhhQ xen/drivers/block/Makefile
3ddb79c2HFkXuRxi1CriJtSFmY6Ybw xen/include/xeno/timer.h
3ddb79c2_m8lT9jDKse_tePj7zcnNQ xen/include/xeno/timex.h
3ddb79c2e2C14HkndNEJlYwXaPrF5A xen/include/xeno/tqueue.h
+403a3edbG9K5uZjuY19_LORbQGmFbA xen/include/xeno/trace.h
3ddb79c1-kVvF8cVa0k3ZHDdBMj01Q xen/include/xeno/types.h
3f055a3dwldYR102YcSuBaxIf9t3Jw xen/include/xeno/vbd.h
3e8827bdaqPeZAWGVOwswgY9bWSx4g xen/include/xeno/version.h
$(MAKE) -C xc
$(MAKE) -C misc
$(MAKE) -C examples
+ $(MAKE) -C xentrace
install: all
$(MAKE) -C balloon install
$(MAKE) -C xc install
$(MAKE) -C misc install
$(MAKE) -C examples install
+ $(MAKE) -C xentrace install
dist: all
$(MAKE) -C balloon dist
$(MAKE) -C xc dist
$(MAKE) -C misc dist
$(MAKE) -C examples dist
+ $(MAKE) -C xentrace dist
clean:
$(MAKE) -C balloon clean
$(MAKE) -C misc clean
$(MAKE) -C examples clean
$(MAKE) -C nsplitd clean
+ $(MAKE) -C xentrace clean
--- /dev/null
+
+CC = gcc
+CFLAGS = -Wall -O3
+CFLAGS += -I../../xen/include -I../../xenolinux-sparse/include
+
+HDRS = $(wildcard *.h)
+OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
+
+BIN = xentrace
+SCRIPTS = $(wildcard *.py)
+
+all: $(BIN)
+
+install: all
+ mkdir -p /usr/bin
+ cp $(BIN) /usr/bin
+ cp $(SCRIPTS) /usr/bin
+ chmod 755 /usr/bin/$(BIN)
+ for i in $(SCRIPTS); do chmod 755 /usr/bin/$$i; done
+
+dist: all
+ mkdir -p ../../../install/bin
+ cp $(BIN) ../../../install/bin
+ chmod 755 ../../../install/bin/$(BIN)
+ cp $(SCRIPTS) ../../../install/bin
+ for i in $(SCRIPTS); do chmod 755 ../../../install/bin/$i; done
+
+clean:
+ $(RM) *.a *.so *.o *.rpm $(BIN)
+
+%: %.c $(HDRS) Makefile
+ $(CC) $(CFLAGS) -o $@ $< -lxc
--- /dev/null
+/******************************************************************************
+ *
+ * tools/xentrace/xentrace.c
+ *
+ * Tool for collecting trace buffer data from Xen.
+ *
+ * Copyright (C) 2004 by Intel Research Cambridge
+ *
+ * Author: Mark Williamson, mark.a.williamson@intel.com
+ * Date: February 2004
+ *
+ *****************************************************************************/
+
+#include <time.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <argp.h>
+#include <signal.h>
+
+#include "../xc/lib/xc_private.h"
+
+#define TRACE_BUFFER /* need to define this for trace.h */
+#include <xeno/trace.h>
+
+extern FILE *stdout;
+
+/***** Compile time configuration of defaults ********************************/
+
+#define NUM_CPUS 1 /* XXX this ought to be removed and replaced with something
+ * cleverer to dynamically query the machine - I'll use a
+ * dom0 op once I've implemented it! :-) */
+
+/* when we've got more records than this waiting, we log it to the output */
+#define NEW_DATA_THRESH 1
+
+/* sleep for this long (milliseconds) between checking the trace buffers */
+#define POLL_SLEEP_MILLIS 100
+
+/***** The code **************************************************************/
+
+typedef struct settings_st {
+ char *outfile;
+ unsigned int num_cpus;
+ struct timespec poll_sleep;
+ unsigned long new_data_thresh;
+} settings_t;
+
+settings_t opts;
+
+int interrupted = 0; /* gets set if we get a SIGHUP */
+
+void close_handler(int signal)
+{
+ interrupted = 1;
+ fprintf(stderr,"Received signal %d, now exiting\n", signal);
+}
+
+/**
+ * millis_to_timespec - convert a time in milliseconds to a struct timespec
+ * @millis: time interval in milliseconds
+ */
+struct timespec millis_to_timespec(unsigned long millis)
+{
+ struct timespec spec;
+
+ spec.tv_sec = millis / 1000;
+ spec.tv_nsec = (millis % 1000) * 1000;
+
+ return spec;
+}
+
+
+/**
+ * print_rec - plain print an event given a pointer to its start
+ * @cpu: CPU the data came from
+ * @data: pointer to the start of the event data
+ * @out: file stream to print out to
+ *
+ * Takes a pointer to a record and prints out the data.
+ */
+void print_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
+{
+ fprintf(out, "%u %llu %lu %lu %lu %lu %lu %lu\n",
+ cpu, rec->cycles, rec->event, rec->d1, rec->d2,
+ rec->d3, rec->d4, rec->d5);
+}
+
+
+/**
+ * get_tbuf_ptrs - get pointer to trace buffers
+ *
+ * Does a dom0 op to fetch a "pointer" to the trace buffers. The pointer can't
+ * be dereferenced immediately, since it is a physical address of memory in Xen
+ * space - they are used in this program to mmap the right area from /dev/mem.
+ */
+struct t_buf *get_tbuf_ptrs()
+{
+ int ret;
+ dom0_op_t op; /* dom0 op we'll build */
+ int xc_handle = xc_interface_open(); /* for accessing control interface */
+
+ op.cmd = DOM0_GETTBUFS;
+ op.interface_version = DOM0_INTERFACE_VERSION;
+
+ ret = do_dom0_op(xc_handle, &op);
+
+ xc_interface_close(xc_handle);
+
+ if(ret) {
+ PERROR("Failure to get trace buffer pointer from Xen");
+ exit(EXIT_FAILURE);
+ }
+
+ return op.u.gettbufs.phys_addr;
+}
+
+/**
+ * map_tbufs - memory map Xen trace buffers into user space
+ * @tbufs: physical address of the trace buffers
+ *
+ * Given the physical address of the Xen trace buffers, maps them into process
+ * address space by memory mapping /dev/mem. Returns a pointer to the location
+ * the buffers have been mapped to.
+ */
+struct t_buf *map_tbufs(struct t_buf *tbufs_phys)
+{
+ int dm_fd; /* file descriptor for /dev/mem */
+ struct t_buf *tbufs_mapped;
+
+ dm_fd = open("/dev/mem", O_RDONLY);
+
+ if(dm_fd < 0) {
+ PERROR("Open /dev/mem when mapping trace buffers\n");
+ exit(EXIT_FAILURE);
+ }
+
+ tbufs_mapped = (struct t_buf *)mmap(NULL, opts.num_cpus * TB_SIZE,
+ PROT_READ, MAP_SHARED,
+ dm_fd, (off_t)tbufs_phys);
+
+ close(dm_fd);
+
+ if(tbufs_mapped == MAP_FAILED) {
+ PERROR("Failed to mmap trace buffers");
+ exit(EXIT_FAILURE);
+ }
+
+ return tbufs_mapped;
+}
+
+
+/**
+ * init_bufs_ptrs - initialises an array of pointers to the trace buffers
+ * @bufs_mapped: the userspace address where the trace buffers are mapped
+ *
+ * Initialises an array of pointers to individual trace buffers within the
+ * mapped region containing all trace buffers.
+ */
+struct t_buf **init_bufs_ptrs(void *bufs_mapped)
+{
+ int i;
+ struct t_buf **user_ptrs;
+
+ user_ptrs = (struct t_buf **)calloc(opts.num_cpus, sizeof(struct t_buf *));
+
+ if(!user_ptrs) {
+ PERROR( "Failed to allocate memory for buffer pointers\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* initialise pointers to the trace buffers - given the size of a trace
+ * buffer and the value of bufs_maped, we can easily calculate these */
+ for(i = 0; i<opts.num_cpus; i++)
+ user_ptrs[i] = (struct t_buf *)(
+ (unsigned long)bufs_mapped + TB_SIZE * i);
+
+ return user_ptrs;
+}
+
+
+/**
+ * init_rec_ptrs - initialises data area pointers to locations in user space
+ * @tbufs_phys: physical base address of the trace buffer area
+ * @tbufs_mapped: user virtual address of base of trace buffer area
+ * @meta: array of user-space pointers to struct t_buf's of metadata
+ *
+ * Initialises data area pointers to the locations that data areas have been
+ * mapped in user space. Note that the trace buffer metadata contains physical
+ * pointers - the array returned allows more convenient access to them.
+ */
+struct t_rec **init_rec_ptrs(struct t_buf *tbufs_phys,
+ struct t_buf *tbufs_mapped,
+ struct t_buf **meta)
+{
+ int i;
+ struct t_rec **data;
+
+ data = calloc(opts.num_cpus, sizeof(struct t_rec *));
+
+ if(!data) {
+ PERROR( "Failed to allocate memory for data pointers\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for(i = 0; i<opts.num_cpus; i++) {
+ data[i] = (struct t_rec *)(
+ (unsigned long)meta[i]->data
+ - (unsigned long)tbufs_phys
+ + (unsigned long)tbufs_mapped
+ );
+ }
+
+ return data;
+}
+
+/**
+ * init_tail_idxs - initialise an array of tail indexes
+ * @bufs: array of pointers to trace buffer metadata in struct t_buf's
+ *
+ * The tail indexes indicate where we're read to so far in the data array of a
+ * trace buffer. Each entry in this table corresponds to the tail index for a
+ * particular trace buffer.
+ */
+int *init_tail_idxs(struct t_buf **bufs)
+{
+ int i;
+ int *tails = calloc(opts.num_cpus, sizeof(unsigned int));
+
+ if(!tails) {
+ PERROR("Failed to allocate memory for tail pointers\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for(i = 0; i<opts.num_cpus; i++)
+ tails[i] = bufs[i]->head;
+
+ return tails;
+}
+
+
+/**
+ * monitor_tbufs - monitor the contents of tbufs and output to a file
+ * @logfile: the FILE * representing the file to log to
+ */
+int monitor_tbufs(FILE *logfile)
+{
+ int i, j;
+ void *tbufs_mapped; /* pointer to where the tbufs are mapped */
+ struct t_buf **meta; /* pointers to the trace buffer metadata */
+ struct t_rec **data; /* pointers to the trace buffer data areas
+ * where they are mapped into user space. */
+ int *tails; /* store tail indexes for the trace buffers */
+ struct t_buf *tbufs_phys; /* physical address of the tbufs */
+
+ /* setup access to trace buffers */
+ tbufs_phys = get_tbuf_ptrs();
+ tbufs_mapped = map_tbufs(tbufs_phys);
+
+ /* build arrays of convenience ptrs */
+ meta = init_bufs_ptrs (tbufs_mapped);
+ data = init_rec_ptrs (tbufs_phys, tbufs_mapped, meta);
+ tails = init_tail_idxs (meta);
+
+ /* now, scan buffers for events */
+ while(!interrupted) {
+ for(i = 0; i < opts.num_cpus; i++) {
+ signed long newdata = meta[i]->head - tails[i];
+ signed long prewrap = newdata;
+
+ /* correct newdata and prewrap in case of a pointer wrap */
+ if(newdata < 0) {
+ newdata += meta[i]->size;
+ prewrap = meta[i]->size - tails[i];
+ }
+
+ if(newdata >= opts.new_data_thresh) {
+ /* output pre-wrap data */
+ for(j = 0; j < prewrap; j++)
+ print_rec(i, data[i] + tails[i] + j, logfile);
+
+ /* output post-wrap data, if any */
+ for(j = 0; j < (newdata - prewrap); j++)
+ print_rec(i, data[i] + j, logfile);
+
+ tails[i] += newdata;
+ if(tails[i] >= meta[i]->size) tails[i] = 0;
+ }
+ }
+ nanosleep(&opts.poll_sleep, NULL);
+ }
+
+ /* cleanup */
+ free(meta);
+ free(data);
+ free(tails);
+ /* don't need to munmap - cleanup is automatic */
+ fclose(logfile);
+
+ return 0;
+}
+
+
+/******************************************************************************
+ * Various declarations / definitions GNU argp needs to do its work
+ *****************************************************************************/
+
+
+/* command parser for GNU argp - see GNU docs for more info */
+error_t cmd_parser(int key, char *arg, struct argp_state *state)
+{
+ settings_t *setup = (settings_t *)state->input;
+
+ switch(key)
+ {
+ case 't': /* set new records threshold for logging */
+ {
+ char *inval;
+ setup->new_data_thresh = strtol(arg, &inval, 0);
+ if(inval == arg) argp_usage(state);
+ }
+
+ break;
+
+ case 's': /* set sleep time (given in milliseconds) */
+ {
+ char *inval;
+ setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0));
+ if(inval == arg) argp_usage(state);
+ }
+ break;
+
+ case 'n': /* set number of CPU trace buffers to map */
+ {
+ char *inval;
+ setup->num_cpus = strtol(arg, &inval, 0);
+ if(inval == arg) argp_usage(state);
+ }
+ break;
+
+ case ARGP_KEY_ARG:
+ if(state->arg_num == 0)
+ setup->outfile = arg;
+ else
+ argp_usage(state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+#define xstr(x) str(x)
+#define str(x) #x
+
+const struct argp_option cmd_opts[] =
+{
+ { .name = "log-thresh", .key='t', .arg="l",
+ .doc =
+ "Set number, l, of new records required to trigger a write to output "
+ "(default " xstr(NEW_DATA_THRESH) ")." },
+
+ { .name = "poll-sleep", .key='s', .arg="p",
+ .doc =
+ "Set sleep time, p, in milliseconds between polling the trace buffer "
+ "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
+
+ { .name = "num-cpus", .key = 'n', .arg="i",
+ .doc =
+ "Set number, i, of CPU trace buffers to map. This should not exceed "
+ "the number of CPUs in the system (default " xstr(NUM_CPUS) ")." },
+
+ {0}
+};
+
+const struct argp parser_def =
+{
+ .options = cmd_opts,
+ .parser = cmd_parser,
+ .args_doc = "[output file]",
+ .doc =
+ "Tool to capure Xen trace buffer data"
+ "\v"
+ "This tool is used to capture trace buffer data from Xen. The data is "
+ "output as space-separated decimal numbers, represented in ASCII, in "
+ "the following order:\n\n"
+ " CPU TSC EVENT DATA1 DATA2 DATA3 DATA4 DATA5\n"
+};
+
+
+const char *argp_program_version = "xentrace v1.0";
+const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
+
+
+int main(int argc, char **argv)
+{
+ int ret;
+ FILE *logfile = stdout;
+
+ const struct sigaction act = { .sa_handler = close_handler };
+
+ opts.outfile = 0;
+ opts.num_cpus = 1;
+ opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
+ opts.new_data_thresh = NEW_DATA_THRESH;
+
+ argp_parse(&parser_def, argc, argv, 0, 0, &opts);
+
+ if(opts.outfile) {
+ logfile = fopen(opts.outfile, "w");
+ }
+
+ /* ensure that if we get a signal, we'll do cleanup, then exit */
+ sigaction(SIGHUP, &act, 0);
+ sigaction(SIGTERM, &act, 0);
+ sigaction(SIGINT, &act, 0);
+
+ ret = monitor_tbufs(logfile);
+
+ return ret;
+}
--- /dev/null
+#!/usr/bin/env python
+
+# by Mark Williamson, (C) 2004 Intel Research Cambridge
+
+# Program for separating trace buffer data into per-cpu log files.
+
+import re, sys, signal
+
+def usage():
+ print >> sys.stderr, \
+ "Usage: " + sys.argv[0] + """ base-name
+ Separates ASCII trace buffer data on stdin into per-CPU trace
+ files. The trace files are named by appending the CPU number
+ to the base name supplied on the command line.
+
+ Depending on your system and the volume of trace buffer data,
+ this script may not be able to keep up with the output of xentrace
+ if it is piped directly. In these circumstances you should have
+ xentrace output to a file for processing off-line.
+ """
+ sys.exit(1)
+
+def sighand(x,y):
+ global interrupted
+ interrupted = 1
+
+signal.signal(signal.SIGTERM, sighand)
+signal.signal(signal.SIGHUP, sighand)
+signal.signal(signal.SIGINT, sighand)
+
+r = re.compile("(\d) .*")
+
+if len(sys.argv) < 2:
+ usage()
+else:
+ base_name = sys.argv[1]
+
+files = {}
+interrupted = 0
+
+while not interrupted:
+ try:
+ line = sys.stdin.readline()
+ if not line: break
+
+ m = r.match(line)
+
+ if not m: print >> sys.stderr, "Invalid input line."
+
+ cpu = m.group(1)
+
+ if not files.has_key(base_name + str(cpu)):
+ files[base_name + str(cpu)] = open(base_name + str(cpu), "w")
+
+ print >> files[base_name + str(cpu)], line,
+
+ except IOError: sys.exit()
+
+# files closed automatically
--- /dev/null
+#!/usr/bin/env python
+
+# by Mark Williamson, (C) 2004 Intel Research Cambridge
+
+# Program for reformatting trace buffer output according to user-supplied rules
+
+import re, sys, string, signal
+
+def usage():
+ print >> sys.stderr, \
+ "Usage: " + sys.argv[0] + """ defs-file
+ Parses trace data in ASCII format and reformats it according to the
+ rules in a file of definitions. The rules in this file should have
+ the format ({ and } show grouping and are not part of the syntax):
+
+ {event_id}{whitespace}{text format string}
+
+ The textual format string may include the format specifiers:
+ %(cpu)s, %(tsc), %(event)s, %(1)s, %(2)s, %(3)s, %(4)s, %(5)s
+
+ Which correspond to the CPU number, event ID, timestamp counter and
+ the 5 data fields from the trace record. There should be one such
+ rule for each type of event.
+
+ Depending on your system and the volume of trace buffer data,
+ this script may not be able to keep up with the output of xentrace
+ if it is piped directly. In these circumstances you should have
+ xentrace output to a file for processing off-line.
+ """
+ sys.exit(1)
+
+def read_defs(defs_file):
+ defs = {}
+
+ fd = open(defs_file)
+
+ reg = re.compile('(\d+)\s+(\S.*)')
+
+ while True:
+ line = fd.readline()
+ if not line:
+ break
+
+ m = reg.match(line)
+
+ if not m: print >> sys.stderr, "Bad format file" ; sys.exit(1)
+
+ defs[m.group(1)] = m.group(2)
+
+ return defs
+
+def sighand(x,y):
+ global interrupted
+ interrupted = 1
+
+##### Main code
+
+if len(sys.argv) < 2:
+ usage()
+
+signal.signal(signal.SIGTERM, sighand)
+signal.signal(signal.SIGHUP, sighand)
+signal.signal(signal.SIGINT, sighand)
+
+interrupted = 0
+
+defs = read_defs(sys.argv[1])
+
+reg = re.compile('(\d+) (\d+) (\d+) (.*)')
+
+while not interrupted:
+ try:
+ line = sys.stdin.readline()
+ if not line:
+ break
+
+ m = reg.match(line)
+
+ if not m: print >> sys.stderr, "Invalid input line."
+
+ s = string.split(m.group(4))
+
+ args = {'cpu' : m.group(1),
+ 'tsc' : m.group(2),
+ 'event' : m.group(3) }
+
+ i = 0
+ for item in s:
+ args[str(i)] = item
+ i += 1
+
+ if defs.has_key(m.group(3)): print defs[m.group(3)] % args
+ # silently skip lines we don't have a format for - a 'complain' option
+ # should be added if needed
+
+ except IOError: sys.exit()
#include <asm/desc.h>
#include <asm/domain_page.h>
#include <asm/pdb.h>
+#include <xeno/trace.h>
struct cpuinfo_x86 boot_cpu_data = { 0 };
/* Lots of nice things, since we only target PPro+. */
#endif
watchdog_on = 1;
+
+#ifdef TRACE_BUFFER
+ init_trace_bufs(); /* initialise trace buffers */
+#endif
}
#include <asm/domain_page.h>
#include <asm/msr.h>
#include <asm/pdb.h>
+#include <xeno/trace.h>
extern unsigned int alloc_new_dom_mem(struct task_struct *, unsigned int);
ret = 0;
}
break;
+
+#ifdef TRACE_BUFFER
+ case DOM0_GETTBUFS:
+ {
+ op->u.gettbufs.phys_addr = get_tb_ptr();
+ copy_to_user(u_dom0_op, op, sizeof(*op));
+ ret = 0;
+ }
+ break;
+#endif
case DOM0_READCONSOLE:
{
--- /dev/null
+/******************************************************************************
+ *
+ * common/trace.c
+ *
+ * Xen Trace Buffer
+ *
+ * Copyright (C) 2004 by Intel Research Cambridge
+ *
+ * Author: Mark Williamson, mark.a.williamson@intel.com
+ * Date: January 2004
+ *
+ * The trace buffer code is designed to allow debugging traces of Xen to be
+ * generated on UP / SMP machines. Each trace entry is timestamped so that
+ * it's possible to reconstruct a chronological record of trace events.
+ *
+ * See also include/xeno/trace.h and the dom0 op in
+ * include/hypervisor-ifs/dom0_ops.h
+ *
+ *****************************************************************************/
+
+#include <xeno/config.h>
+
+#ifdef TRACE_BUFFER /* don't compile this stuff in unless explicitly enabled */
+
+#include <asm/timex.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <xeno/lib.h>
+#include <xeno/sched.h>
+#include <xeno/slab.h>
+#include <xeno/smp.h>
+#include <xeno/spinlock.h>
+#include <xeno/trace.h>
+#include <asm/atomic.h>
+
+
+
+/* Pointers to the meta-data objects for all system trace buffers */
+struct t_buf *t_bufs[NR_CPUS];
+
+/* a flag recording whether initialisation has been done */
+atomic_t tb_init_done = ATOMIC_INIT(0);
+
+
+/**
+ * init_trace_bufs - performs initialisation of the per-cpu trace buffers.
+ *
+ * This function is called at start of day in order to initialise the per-cpu
+ * trace buffers. The trace buffers are then available for debugging use, via
+ * the %TRACE_xD macros exported in <xeno/trace.h>.
+ */
+void init_trace_bufs()
+{
+ int i;
+ void *d; /* trace buffer area pointer */
+
+ d = kmalloc(smp_num_cpus * TB_SIZE, GFP_KERNEL);
+
+ if( d == NULL ) {
+ printk("Xen trace buffers: memory allocation failed\n");
+ return;
+ }
+
+ for(i = 0; i < smp_num_cpus; i++) {
+ struct t_buf *buf = t_bufs[i]
+ = (struct t_buf *)( (unsigned int)d + TB_SIZE * i );
+
+ /* for use in Xen */
+ buf->vdata = (struct t_rec *)
+ ( (unsigned int)buf + sizeof(struct t_buf) );
+ buf->head_ptr = buf->vdata;
+ spin_lock_init(&buf->lock);
+
+ /* for use in user space */
+ buf->data = (struct t_rec *)__pa(buf->vdata);
+ buf->head = 0;
+
+ /* for use in both */
+ buf->size = (TB_SIZE - sizeof(struct t_buf)) / sizeof(struct t_rec);
+ }
+
+ printk("Xen trace buffers: initialised\n");
+
+ wmb(); /* above must be visible before tb_init_done flag set */
+
+ atomic_set(&tb_init_done, 1);
+}
+
+
+
+/**
+ * get_tb_ptr - return physical address of the trace buffers.
+ *
+ * Called by the %DOM0_GETTBUFS dom0 op to fetch the physical address of the
+ * trace buffers.
+ */
+struct t_buf *get_tb_ptr()
+{
+ /* a physical address (user space maps this using /dev/mem) */
+ return (struct t_buf *)__pa(t_bufs[0]);
+}
+
+#endif /* #ifdef TRACE_BUFFER */
* This makes sure that old versions of dom0 tools will stop working in a
* well-defined way (rather than crashing the machine, for instance).
*/
-#define DOM0_INTERFACE_VERSION 0xAAAA0005
+#define DOM0_INTERFACE_VERSION 0xAAAA0006
/*
int cpu; /* -1 implies unpin */
} dom0_pincpudomain_t;
+/* Get trace buffers physical base pointer */
+#define DOM0_GETTBUFS 21
+typedef struct dom0_gettbufs_st
+{
+ /* OUT variable - location of the trace buffers */
+ struct t_buf *phys_addr;
+} dom0_gettbufs_t;
+
typedef struct dom0_op_st
{
unsigned long cmd;
dom0_settime_t settime;
dom0_readconsole_t readconsole;
dom0_pincpudomain_t pincpudomain;
+ dom0_gettbufs_t gettbufs;
} u;
} dom0_op_t;
#define DPRINTK(_f, _a...) printk("(file=%s, line=%d) " _f, \
__FILE__ , __LINE__ , ## _a )
#define STACK_GUARD
+#define TRACE_BUFFER
#else
#define DPRINTK(_f, _a...) ((void)0)
#endif
--- /dev/null
+/******************************************************************************
+ *
+ * include/xeno/trace.h
+ *
+ * Xen Trace Buffer
+ *
+ * Copyright (C) 2003 by Intel Research Cambridge
+ *
+ * Author: Mark Williamson, mark.a.williamson@intel.com
+ * Date: January 2004
+ *
+ * The trace buffer code is designed to allow debugging traces of Xen to be
+ * generated on UP / SMP machines. Each trace entry is timestamped so that
+ * it's possible to reconstruct a chronological record of trace events.
+ *
+ * Access to the trace buffers is via a dom0 hypervisor op and analysis of
+ * trace buffer contents can then be performed using a userland tool.
+ *
+ * See also common/trace.c and the dom0 op in include/hypervisor-ifs/dom0_ops.h
+ *
+ *****************************************************************************/
+
+#ifdef TRACE_BUFFER
+
+#ifndef __XENO_TRACE_H__
+#define __XENO_TRACE_H__
+
+#ifdef __KERNEL__
+
+#include <xeno/spinlock.h>
+#include <asm/page.h>
+#include <xeno/types.h>
+#include <xeno/sched.h>
+#include <asm/atomic.h>
+#include <asm/current.h>
+#include <asm/msr.h>
+
+#endif /* #ifdef __KERNEL__ */
+
+/******************************************************************************
+ * Data structure declarations
+ *****************************************************************************/
+
+/* This structure represents a single trace buffer record. */
+struct t_rec {
+ u64 cycles; /* 64 bit cycle counter timestamp */
+ u32 event; /* 32 bit event ID */
+ u32 d1, d2, d3, d4, d5; /* event data items */
+};
+
+/* This structure contains the metadata for a single trace buffer. The head
+ * field, indexes into an array of struct t_rec's.
+ */
+struct t_buf {
+ struct t_rec *data; /* pointer to data area. physical address
+ * for convenience in user space code */
+
+ unsigned int size; /* size of the data area, in t_recs */
+ unsigned int head; /* array index of the most recent record */
+
+#ifdef __KERNEL__
+ struct t_rec *head_ptr; /* pointer to the head record */
+ struct t_rec *vdata; /* virtual address pointer to data,
+ * for use in Xen */
+ spinlock_t lock; /* ensure mutually exlusive access (for inserts) */
+#endif /* #ifdef __KERNEL__ */
+
+ /* never add anything here - the kernel stuff must be the last elements */
+};
+
+/******************************************************************************
+ * Functions
+ *****************************************************************************/
+
+#ifdef __KERNEL__
+
+/* Used to initialise trace buffer functionality */
+void init_trace_bufs();
+
+/* used to retrieve the physical address of the trace buffers */
+struct t_buf *get_tb_ptr();
+
+/**
+ * trace - Enters a trace tuple into the trace buffer for the current CPU.
+ * @event: the event type being logged
+ * @d1...d5: the data items for the event being logged
+ *
+ * Logs a trace record into the appropriate buffer. Returns nonzero on
+ * failure, otherwise 0. Failure occurs only if the trace buffers are not yet
+ * initialised.
+ */
+static inline int trace(u32 event, u32 d1, u32 d2, u32 d3, u32 d4, u32 d5)
+{
+ extern struct t_buf *t_bufs[]; /* global array of pointers to bufs */
+ extern atomic_t tb_init_done; /* set when buffers are initialised */
+ unsigned long flags; /* for saving interrupt flags */
+ struct t_buf *buf; /* the buffer we're working on */
+ struct t_rec *rec; /* next record to fill out */
+
+
+ if(!atomic_read(&tb_init_done)) return -1;
+
+ buf = t_bufs[smp_processor_id()];
+ rec = buf->head_ptr;
+
+ spin_lock_irqsave(&buf->lock, flags);
+ /* interrupts _disabled locally_ during the following code */
+
+ rdtscll(rec->cycles);
+ rec->event = event;
+ rec->d1 = d1;
+ rec->d2 = d2;
+ rec->d3 = d3;
+ rec->d4 = d4;
+ rec->d5 = d5;
+
+ wmb(); /* above must be visible before reader sees index updated */
+
+ if( likely( buf->head_ptr < ( buf->vdata + buf->size - 1) ) ) {
+ buf->head_ptr++;
+ buf->head++;
+ } else {
+ buf->head = 0;
+ buf->head_ptr = buf->vdata;
+ }
+
+ spin_unlock_irqrestore(&buf->lock, flags);
+ /* Interrupts now _re-enabled locally_ */
+
+ return 0;
+}
+
+
+#endif /* #ifdef __KERNEL__ */
+
+
+/******************************************************************************
+ * Macros
+ *****************************************************************************/
+
+/* How much space is allowed for a single trace buffer, including data and
+ * metadata (and maybe some waste).
+ */
+#define TB_SIZE PAGE_SIZE
+
+#ifdef __KERNEL__
+
+/* avoids troubling the caller with casting their arguments to a trace macro */
+#define trace_do_casts(e,d1,d2,d3,d4,d5) \
+ trace(e, \
+ (unsigned long)d1, \
+ (unsigned long)d2, \
+ (unsigned long)d3, \
+ (unsigned long)d4, \
+ (unsigned long)d5)
+
+/* convenience macros for calling the trace function */
+#define TRACE_0D(event) trace_do_casts(event,0, 0, 0, 0, 0 )
+#define TRACE_1D(event,d) trace_do_casts(event,d, 0, 0, 0, 0 )
+#define TRACE_2D(event,d1,d2) trace_do_casts(event,d1,d2,0, 0, 0 )
+#define TRACE_3D(event,d1,d2,d3) trace_do_casts(event,d1,d2,d3,0, 0 )
+#define TRACE_4D(event,d1,d2,d3,d4) trace_do_casts(event,d1,d2,d3,d4,0 )
+#define TRACE_5D(event,d1,d2,d3,d4,d5) trace_do_casts(event,d1,d2,d3,d4,d5)
+
+#endif /* #ifdef __KERNEL__ */
+
+#endif /* #ifndef __XENO_TRACE_H__ */
+
+#else /* #ifdef TRACE_BUFFER */
+
+/* define out macros so that they can be left in code when tracing is disabled */
+#define TRACE_0D(event) ((void)0)
+#define TRACE_1D(event,d) ((void)0)
+#define TRACE_2D(event,d1,d2) ((void)0)
+#define TRACE_3D(event,d1,d2,d3) ((void)0)
+#define TRACE_4D(event,d1,d2,d3,d4) ((void)0)
+#define TRACE_5D(event,d1,d2,d3,d4,d5) ((void)0)
+
+#endif /* #ifdef TRACE_BUFFER */